package org.zachary.imitation.raft;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.zachary.utils.lang.StringUtils;

import java.io.Serializable;

/**
 * 对节点的 {@link Endpoint} 的信息进行封装扩展的类，除了 {@link Endpoint} 中存放节点的 ip 和 port 外（PeerId 持有 Endpoint 类型的成员变量），其它对节点的扩展属性放到 {@link PeerId} 类中。
 *
 * 基于 raft 论文中的算法的描述 ，{@link Endpoint} 已经足够描述一个节点的信息了。但可以对节点信息进行进一步的优化，集群中的每个节点都可以成为 Leader，但如果有一台节点的配置特别高，那么这个节点更有理由成为 Leader。
 * 为了表达配置特别高的节点更有可能成为 Leader，可以为节点额外增加一个 priority 属性，它的作用基本上等同于 ZAB 的 serverID/myid。
 *
 * @see Endpoint
 *
 * Created by guozhaokun on 2024/8/30 15:02.
 */
public class PeerId implements Serializable {
	private Logger logger = LoggerFactory.getLogger(PeerId.class);

	public static final String IP_ANY = "0.0.0.0";

	private int idx;
	/** 当前节点 ip 和 port */
	private Endpoint endpoint = new Endpoint(IP_ANY, 0);
	/** 选举时节点的优先级，初始化时设置为不按优先级进行选举 */
	private int priority = ElectionPriority.DISABLED;

	public Endpoint getEndpoint() {
		return endpoint;
	}

	public PeerId setEndpoint(Endpoint endpoint) {
		this.endpoint = endpoint;
		return this;
	}

	public int getPriority() {
		return priority;
	}

	public PeerId setPriority(int priority) {
		this.priority = priority;
		return this;
	}

	/** 该方法是本类最核心的方法，将传入进来的字符串解析为一个 PeerId 对象 */
	public boolean parse(final String s) {
		if (StringUtils.isEmpty(s)) {
			return false;
		}
		// 传进来的字符串的形式是 ip:port:idx:priority，实际上可以只传 ip:port，idx 和 priority 是选择性配置的。当一个进程启动了多个节点，则需要通过 idx 来标识不同的节点
		final String[] tmps = s.split(":");
		if (tmps.length < 2 || tmps.length > 4) {
			return false;
		}
		try {
			final int port = Integer.parseInt(tmps[1]);
			this.endpoint = new Endpoint(tmps[0], port);
			switch (tmps.length) {
				case 3:
					// 如果数组长度为 3，说明用户配置了 idx，给 idx 赋值即可
					this.idx = Integer.parseInt(tmps[2]);
					break;
				case 4:
					// 如果数组长度为 4，则说明 idx 和 priority 都被配置了，先判断了一下 idx 是否为空
					if (tmps[2].equals("")) {
						this.idx = 0;
					} else {
						this.idx = Integer.parseInt(tmps[2]);
					}
					this.priority = Integer.parseInt(tmps[3]);
					break;
				default:
					break;
			}
			return true;
		} catch (final Exception e) {
			logger.error("Parse peer from string failed: {}.", s, e);
			return false;
		}
	}
}

